#ifndef COMMON_ATOMIC_H_
#define COMMON_ATOMIC_H_
#include <cstdint>
#include <cstddef>
#include <type_traits>


namespace fermat {

// false
template<typename T, bool = std::is_integral<T>::value && !std::is_same<T, bool>::value >

class atomic_base {
public:
	atomic_base(): _value(0)  {  }
	atomic_base(T v): _value(v) { }

	bool is_lock_free()
	{
		 return true;
	}

	void store(T newValue)
	{
		__sync_lock_test_and_set(&_value, newValue);
	}
	T load()
	{
	    return __sync_val_compare_and_swap(&_value, 0, 0);
	}

	operator T() { return load(); }

	T exchange(T newcvalue)
	{
		return __sync_lock_test_and_set(&_value, newcvalue);  	
	}

	bool compare_exchange(T old, T n)
	{
		return __sync_bool_compare_and_swap(&_value, old, n);
	}

	bool test(T x)
	{
		return __sync_bool_compare_and_swap(&_value,x,x);
	}
	

	volatile T _value;

private:
	atomic_base(atomic_base&);
	atomic_base& operator=(const atomic_base& );
};

//integer
template <typename T>
class atomic_base<T, true>: public atomic_base<T, false> {
public:
	typedef atomic_base<T, false> BASE;
	atomic_base():BASE() {}
	atomic_base(T v):BASE(v) {}

	T fetch_add(T x)
	{  
	  	return __sync_fetch_and_add(&this->_value, x);
	}

	T fetch_sub(T x)
	{
		return fetch_add(-x);
	}

	T fetch_and(T x) 
	{
		return __sync_fetch_and_and(&this->_value, x);	
	}

	T fetch_or(T x) 
	{
		return __sync_fetch_and_or(&this->_value, x);
	}

	T fetch_xor(T x)
	{
		return __sync_fetch_and_xor(&this->_value, x);
	}

	T add_fetch(T x)
	{
		 return  __sync_add_and_fetch(&this->_value, x);
	}

	T sub_fetch(T x)
	{
		 return __sync_sub_and_fetch(&this->_value, x);
	} 
	
	T and_fetch(T x) 
	{
		return f__sync_and_and_fetch(&this->_value, x);
	}
	
	T or_fetch(T x) 
	{
		return f__sync_or_and_fetch(&this->_value, x);
	}

	T xor_fetch(T x) 
	{
		return f__sync_xor_and_fetch(&this->_value, x);
	}

	T increment_fetch()  {    return add_fetch(T(1));  }
	T decrement_fetch()  {    return sub_fetch(T(1));  }

	T fetch_increment() { return fetch_add(T(1)); }
	T fetch_decrement() { return fetch_sub(T(1)); }

	void add(T x)  {    fetch_add(x);  }

	void increment()  {    increment_fetch();  }

	void decrement()  {    decrement_fetch();  }
	
	T operator++(int) { return  fetch_add(T(1)); }
	T operator++() {  return add_fetch(T(1)); }
	T operator--(int) { return fetch_sub(T(1)); }
	T operator--() { return sub_fetch(T(1)); }
	T operator+=(T x) { return add_fetch(x); } 
	T operator-=(T x) { return sub_fetch(x); } 
	T operator&=(T x) { return and_fetch(x); }
	T operator|=(T x) { return or_fetch(x); }
	T operator^=(T x) { return xor_fetch(x); }

};

template <typename T>
class atomic :public atomic_base<T> {
public:
	typedef atomic_base<T> BASE;
	atomic():BASE() {}
	atomic(T v) : BASE(v) { }

	T operator=(T v) 
	{
		this->store(v);
		return v;
	}
};

template <typename T>

class atomic<T*> :public atomic_base<T*> {
public:
	typedef atomic_base<T*> BASE;
	atomic(): BASE() { }
	atomic(T *v):BASE(v) {}

	T* fetch_add(ptrdiff_t x)
	{  
	  	return __sync_fetch_and_add(&this->_value, x);
	}

	T* fetch_sub(ptrdiff_t x)
	{
		return fetch_add(-x);
	}

	T* add_fetch(ptrdiff_t x)
	{
		 return  __sync_add_and_fetch(&this->_value, x);
	}

	T* sub_fetch(ptrdiff_t x)
	{
		 return f__sync_sub_and_fetch(&this->_value, x);
	} 

	T* operator++(int) { return  fetch_add(T(1)); }
	T* operator++() {  return add_fetch(T(1)); }
	T* operator--(int) { return fetch_sub(T(1)); }
	T* operator--() { return sub_fetch(T(1)); }
	T* operator+=(ptrdiff_t x) { return add_fetch(x); } 
	T* operator-=(ptrdiff_t x) { return sub_fetch(x); } 	

	T* operator=(T *v)
	{
		this->store(v);
		return v;
	}
};

typedef atomic<char>               atomic_char;
typedef atomic<signed char>        atomic_schar;
typedef atomic<unsigned char>      atomic_uchar;
typedef atomic<short>              atomic_short;
typedef atomic<unsigned short>     atomic_ushort;
typedef atomic<int>                atomic_int;
typedef atomic<unsigned int>       atomic_uint;
typedef atomic<long>               atomic_long;
typedef atomic<unsigned long>      atomic_ulong;
typedef atomic<long long>          atomic_llong;
typedef atomic<unsigned long long> atomic_ullong;

typedef atomic<int8_t>             atomic_int8;
typedef atomic<uint8_t>            atomic_uint8;
typedef atomic<int16_t>            atomic_int16;
typedef atomic<uint16_t>           atomic_uint16;
typedef atomic<int32_t>            atomic_int32;
typedef atomic<uint32_t>           atomic_uint32;
typedef atomic<int64_t>            atomic_int64;
typedef atomic<uint64_t>           atomic_uint64;
typedef atomic<bool>               atomic_bool;

//typedef atomic<intptr_t>           atomic_intptr_t;
//typedef atomic<uintptr_t>          atomic_uintptr_t;
typedef atomic<size_t>             atomic_size_t;
typedef atomic<ptrdiff_t>          atomic_ptrdiff_t;

}
#endif
